package com.iflyings.android.util

import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.database.ContentObserver
import android.net.Uri
import android.provider.Settings
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import java.io.*
import java.math.BigInteger
import java.security.MessageDigest

fun fileCopyFlow(src: File, dst: File) =
    flow {
        var fis: InputStream? = null
        var fos: OutputStream? = null

        try {
            emit(0)
            fis = FileInputStream(src.absolutePath)
            fos = FileOutputStream(dst.absolutePath)
            val buffer = ByteArray(100 * 1024)
            val bufferCount = fis.available()
            var copySize = 0
            var copyProgress = 0

            var nread: Int
            while (fis.read(buffer, 0, buffer.size).also { nread = it } != -1) {
                fos.write(buffer, 0, nread)
                copySize += nread

                val process = (copySize * 100f / bufferCount).toInt()
                if (copyProgress != process) {
                    copyProgress = process
                    emit(copyProgress)
                }
            }
            emit(100)
        } catch (e: IOException) {
            e.printStackTrace()
            throw e
        } finally {
            fis?.close()
            fos?.close()
        }
    }.flowOn(Dispatchers.IO)

fun fetchFileMD5Flow(file: File, onChecked: (String) -> Unit) =
    flow {
        val md = MessageDigest.getInstance("MD5")
        val buffer = ByteArray(1024)

        var hasReadSize = 0
        var readProgress = 0

        FileInputStream(file).use { fis ->
            val fileSize = fis.available()

            var length: Int
            while ((fis.read(buffer).also { length = it }) != -1) {
                md.update(buffer, 0, length)
                hasReadSize += length

                val progress = (hasReadSize * 100f / fileSize).toInt()
                if (readProgress != progress) {
                    readProgress = progress
                    emit(readProgress)
                }
            }
        }

        val bi = BigInteger(1, md.digest())
        onChecked(bi.toString(16).padStart(32, '0'))

        emit(100)
    }.flowOn(Dispatchers.IO)

fun fetchSettingsFlow(context: Context, name: String) = callbackFlow {
    trySendBlocking(Settings.System.getInt(context.contentResolver, name))
    val observer = object: ContentObserver(null) {
        override fun onChange(selfChange: Boolean, uri: Uri?) {
            trySendBlocking(Settings.System.getInt(context.contentResolver, name))
        }
    }
    context.contentResolver.registerContentObserver(
        Settings.System.getUriFor(name),
        false, observer)
    awaitClose { context.contentResolver.unregisterContentObserver(observer) }
}

@SuppressLint("UnspecifiedRegisterReceiverFlag")
fun fetchReceiverFlow(context: Context, filter: IntentFilter) = callbackFlow {
    val receiver = object: BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent) {
            trySendBlocking(intent)
        }
    }
    context.registerReceiver(receiver, filter)
    awaitClose { context.unregisterReceiver(receiver) }
}